package com.canyou.filter;

import cn.hutool.core.map.MapUtil;
import com.canyou.utils.IpUtils;
import com.canyou.utils.Oauth2Utils;
import com.canyou.utils.StringUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.MDC;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * 请求响应日志记录
 *
 * @author fy
 * @date 19/10/29 16:51
 */
@Profile("!(dev|test)")
@Component
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class LogFilter extends OncePerRequestFilter {

    private static final Marker ELK_MARKER = MarkerFactory.getMarker("elk");
    private static final String TRACE_ID = "X-B3-TraceId";
    private static final String USER_NAME = "X-B3-Username";
    private static final String IP = "X-B3-Ip";

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        genXb3(request);
        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
        try {
            // 记录请求信息
            before(requestWrapper);
        } catch (Exception ex) {
            log.error("记录请求信息异常: " + ExceptionUtils.getStackTrace(ex), ex);
        }
        chain.doFilter(requestWrapper, responseWrapper);
        try {
            // 记录响应信息
            after(responseWrapper);
        } catch (Exception ex) {
            log.error("记录响应信息异常: " + ExceptionUtils.getStackTrace(ex), ex);
        }
        responseWrapper.copyBodyToResponse();
    }

    /**
     * 生成traceId, 请求的ip和用户写入MDC
     *
     * @param request
     */
    private void genXb3(HttpServletRequest request) {
        String traceId = StringUtils.uuid();
        String username = Oauth2Utils.getUserName();
        String ip = IpUtils.getIpAddr(request);
        MDC.put(TRACE_ID, traceId);
        MDC.put(USER_NAME, username);
        MDC.put(IP, ip);
    }

    /**
     * 记录请求信息
     */
    private void before(HttpServletRequest servletRequest) throws IOException {
        ContentCachingRequestWrapper request = WebUtils.getNativeRequest(servletRequest, ContentCachingRequestWrapper.class);
        Map<String, String> headers = new HashMap<>(30);
        Enumeration<String> headerNames = request.getHeaderNames();

        if (headerNames != null) {
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                headers.put(headerName, request.getHeader(headerName));
            }
        }

        Map<String, Object> requestMap = MapUtil.of("SessionId", request.getSession().getId());
        requestMap.put("Protocol", request.getProtocol());
        requestMap.put("RequestURL", request.getRequestURL());
        requestMap.put("Method", request.getMethod());
        requestMap.put("ContentType", request.getContentType());
        requestMap.put("CharacterEncoding", request.getCharacterEncoding());
        requestMap.put("Locale", request.getLocale());
        requestMap.put("ContentLength", request.getContentLength());
        requestMap.put("RemoteAddr", request.getRemoteAddr());
        requestMap.put("RemoteHost", request.getRemoteHost());
        requestMap.put("RemotePort", request.getRemotePort());
        requestMap.put("Scheme", request.getScheme());
        requestMap.put("ServerName", request.getServerName());
        requestMap.put("ServerPort", request.getServerPort());
        requestMap.put("ContextPath", request.getContextPath());
        requestMap.put("RequestURI", request.getRequestURI());
        requestMap.put("QueryString", request.getQueryString());
        requestMap.put("Cookies", request.getCookies());
        requestMap.put("Headers", headers);
        requestMap.put("ParameterMap", new HashMap<>(request.getParameterMap()));
        String contentType = request.getContentType();
        if (StringUtils.isNotBlank(contentType) && contentType.contains(MediaType.APPLICATION_JSON.toString())) {
            String body = IOUtils.toString(request.getContentAsByteArray(), request.getCharacterEncoding());
            requestMap.put("RequestPayload", body);
        }

        log.info(ELK_MARKER, "\n请求:\n" + objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(requestMap));

    }

    /**
     * 记录响应信息
     */
    private void after(HttpServletResponse servletResponse) throws IOException {
        // ContentCachingResponseWrapper response = new ContentCachingResponseWrapper(servletResponse);
        ContentCachingResponseWrapper response = WebUtils.getNativeResponse(servletResponse, ContentCachingResponseWrapper.class);
        if (response == null) {
            return;
        }
        Map<String, String> headers = new HashMap<>(10);
        Collection<String> headerNames = response.getHeaderNames();
        if (headerNames != null) {
            for (String headerName : headerNames) {
                headers.put(headerName, response.getHeader(headerName));
            }
        }
        Map<String, Object> responseMap = MapUtil.of("Status", response.getStatus());
        responseMap.put("ContentType", response.getContentType());
        responseMap.put("CharacterEncoding", response.getCharacterEncoding());
        responseMap.put("Locale", response.getLocale());
        responseMap.put("Headers", headers);
        String contentType = response.getContentType();
        if (StringUtils.isNotBlank(contentType) && contentType.contains(MediaType.APPLICATION_JSON.toString())) {
            String body = IOUtils.toString(response.getContentAsByteArray(), response.getCharacterEncoding());
            responseMap.put("ResponsePayload", body);
        }

        log.info(ELK_MARKER, "\n响应:\n" + objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(responseMap));
    }

}
